#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G22 -- S12 ------------------------------------------------------------##
__all__ = []

from .. import formlib as layout
from .. import autostacks
from .. import comparecards as cmp_cards
from .. import helpstrings as hs
from .. import pilelib as stack
from .. import stackfill as fill
from ..forms import canfield as CanfieldLayout
from ..gamelib import state, info as gi, game
from ..hint import DefaultHint, CautiousDefaultHint
from ..packlib import place, info as cs
from ..piles import numerica as NumericaStacks
from ..utillib.misc import kwdefault


GT, SL = gi.GT, gi.SL
StackWrapper = stack.StackWrapper


class Gloaming_Hint(NumericaStacks.Hint):

    def step050(self, reserves, rows):
        """
        step050(reserves:list(Stack ...), rows:list(Stack ...)) -> None

        Calculate and score **reserves** build on **rows**. (10000 .. 19999)

        Overide when **reserves** builds **rows**, sum score base on comparison
        of cards in each then **add_hint** with each possible move.
        """
        if not reserves:
            return
        for from_stack in (s for s in reserves if s.cards):
            for to_stack in rows:
                if to_stack.cards:
                    from_rank, to_rank = (from_stack.BASECARD['rank'],
                                          to_stack.TOPCARD['rank'],)
                    self._score = 30000
                    if to_rank < from_rank:
                        self._score = 10000 + to_rank - len(to_stack.cards)
                    elif to_rank == from_rank:
                        self._score -= 10000
                    else:
                        self._score -= 1000 * (to_rank - from_rank)
                        self.add_hint(1, from_stack, to_stack)
                else:
                    self._score = 90000 + to_rank
                    self.add_hint(1, from_stack, to_stack)


class Toad_Talon(stack.DealReserveTalon):
    """
    Deal cards on _reserve_ when all gap.
    """

    def can_deal_cards(self):
        """
        can_deal_cards() -> bool

        Usually informs via Game.can_deal_cards() if pile can deal more
        cards. In addition, make sure all pile is a gap.

        :return: deal more cards?
        :rtype: True | False
        """
        return (super(Toad_Talon, self).can_deal_cards() and
                all([not r.cards for r in self._piles]))


class OpenRedealTalon(stack.OpenAutoMethods, stack.OpenDragMethods,
                      stack.RedealTalon):
    """
    Build none. Play top. Gap reset _row_.
    """
    pass


class Doublets_Foundation(stack.Vary_RK_Foundation):
    """
    Build up in rank multiple.
    """

    def _sequence(self, cards):
        """
        _sequence(cards:seq) -> bool

        :param sequence cards: cards from sending pile
        :return: accept card sequence acccording to *capabilities*?
        :rtype: boolean

        important:: This method should be defined in any subclass.
        """
        return cmp_cards.isRankMultipleSequence(cards, **self.cap)


class Numerica_RowStack(stack.FromTalonWasteBuildMethod, stack.BasicRowStack):
    """
    Build any from _waste_ when exist else _talon_. _BOTTOM_IMAGE_  is shade
    """
    _BOTTOM_IMAGE_ = 'shade'

    @property
    def _helpString(self):
        """
        _helpString -> str

        :return: helpful string about pile for its canvas label.
        :rtype:  base_string
        """
        return hs.BUILD_REGARDLESS


class Gloaming_RowStack(stack.FromReserveBuildMethod, Numerica_RowStack):
    """
    Build any from _reserve_. _BOTTOM_IMAGE_  is shade
    """


class LastChance_RowStack(Numerica_RowStack):
    """
    Build any from _waste_ when exist else _talon_ while has cards.
    _BOTTOM_IMAGE_ is shade
    """

    def _isInFromStacks(self, stack):
        """
        _isFromStack(stack:obj) -> bool

        Piles cards must be played from _waste_ when pile has cards.

        :param object from_stack: another pile for subclass usage
        :param tuple cards: cards to accept from another pile
        :return: cards acceptable?
        :rtype: boolean
        """
        return (not self.cards or
                super(LastChance_RowStack, self)._isInFromStacks(stack))


class Strategerie_RowStack(Numerica_RowStack):
    """
    Build any from _reserve_ and _waste_ or when exist else _talon_.
    _BOTTOM_IMAGE_ is shade
    """

    def _isFromStack(self, stack):
        """
        _isFromStack(stack:obj) -> bool

        Piles cards must be played from _reserve_ and _waste_ when exist else
        _talon_.

        :param object from_stack: another pile for subclass usage
        :param tuple cards: cards to accept from another pile
        :return: cards acceptable?
        :rtype: boolean
        """
        return (self.game.s['reserves'] +
                super(LastChance_RowStack, self)._isInFromStacks(stack))


class Shifting_RowStack(Numerica_RowStack):
    """
    Build any from _waste_ and down in rank alone from _row_. Play top. Base
    KING when from _row_ else any. _BOTTOM_IMAGE_ is shade
    """

    def cardsBuild(self, from_stack, cards):
        """
        cardsBuild(from_stack:obj, cards:seq) -> bool

        Checks parent *capability* with cards _rows_ or _waste_ and sets
        cap['base_rank'] to KING if from _rows_ else ANY. Check rank seq when
        from _rows_.

        :param object from_stack: another pile for subclass usage
        :param tuple cards: cards to accept from another pile
        :return: cards acceptable?
        :rtype: boolean
        """
        waste = self.s['wastes'][-1]
        self.cap['base_rank'] = cs.ANY if from_stack is waste else cs.KING
        if super(Shifting_RowStack, self).cardsBuild(from_stack, cards):
            return False
        return (from_stack == waste or
                cmp_cards.isRankSequence(self.cards[-1:] + cards, **self.cap))

    def _isFromStack(self, stack):
        """
        _isFromStack(stack:obj) -> bool

        Piles cards must be played from _reserve_ and _waste_ when exist else
        _talon_.

        :param object from_stack: another pile for subclass usage
        :param tuple cards: cards to accept from another pile
        :return: cards acceptable?
        :rtype: boolean
        """
        return (self.game.s['rows'] +
                super(Shifting_RowStack, self)._isInFromStacks(stack))


class Assembly_RowStack(stack.GapFromWasteBuildMethod, stack.RK_RowStack):
    """
    Build down in rank alone. Base from _waste_.
    """
    pass


class CircleNine_RowStack(Numerica_RowStack):
    """
    Build any from _waste_ when exist else _talon_. _BOTTOM_IMAGE_ is None
    """
    _BOTTOM_IMAGE_ = None


class Strategerie_Reserve(stack.FromTalonBuildMethod, stack.ReserveStack):
    """
    Build 1 card from _talon_.
    """
    pass


class LastChance_Reserve(stack.OpenStack):
    """
    Build none. Play top. Flip after _talon_ and _waste_ are gap.
    """

    def cardFlips(self):
        """
        cardFlips() -> bool

        Check if pile can flip face down top card. After _talon_ and _waste_
        are gap allow flip.

        :return: pile flips top card?
        :rtype: boolean
        """
        if not super(LastChance_Reserve, self).cardFlips():
            return False
        s = self.game.s
        return not any(p.cards for p in (s['talon'], s['wastes'][-1],))


class Numerica(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 horz. Build any from _waste_. Play top.

    _talon_
      Pile 1 E. Deal 1 card on _waste_.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 4 horz N _row_. Build up in rank alone. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Numerica_RowStack (max_accept=1)
    :cvar Stack _reserves_: None
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=257, name='Numerica', game_type=(GT.NUMERICA |
                    GT.CONTRIB | GT.GNOME), decks=1, skill_level=SL.BALANCED,
                    altnames=('Sir Tommy',), version='2.00')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = StackWrapper(stack.WasteStack, max_cards=1)
    _foundation_ = stack.RK_Foundation
    _row_ = StackWrapper(Numerica_RowStack, max_accept=1)
    _reserve_ = None
    _hint_ = NumericaStacks.Hint

    def _create_game(self, rows=4, playcards=20, **kw):
        l, s = layout.Layout(self), self.s
        max_rows = max(rows, l.NSUITS)
        dx = l.XM + 3 * l.XS / 2
        self.table_size = (l.XM + dx + max_rows * l.XS,
                           l.YM + l._get_stack_height(playcards, l.YS),)
        center = lambda p: (max_rows - p) / 2
        x, y = dx + center(l.NSUITS) * l.XS, l.YM
        for suit in l.SUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                      suit=suit))
            x += l.XS
        x = dx + center(rows) * l.XS
        y += l.YS
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, self))
            x += l.XS
        self._set_region(s['rows'], l._region(x=dx - l.HCW, y=y - l.HCH))
        x, y = l.XM, l.YM
        anchor = 'n'
        if self._reserve_:
            s['reserves'].append(self._reserve_(l.XM, l.YM, game=self))
            y += l.YS / 2
            anchor = 'ne'
        y += l.YS
        s.talon = self._talon_(x, y, game=self)
        y += l.YS
        s['wastes'].append(self._waste_(x, y, game=self))
        if self._waste_ and s['wastes'][-1].cap['max_cards'] > 1:
            l.set_ncards_text(s['waste'][-1], 'ne')
            anchor = 'ne'
        if not issubclass(s['talon'], stack.InitialDealTalon):
            l.set_ncards_text(s['talon'], anchor)
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].deal_cards()

    @property
    def _highlight_piles(self):
        """
        Get stacks to highlight for possible move.

        :return: empty.
        :rtype: tuple
        """
        return ()


class Numerica2Decks(Numerica):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 6 horz. Build any from _waste_. Play top.

    _talon_
      Pile 1 E. Deal 1 card on _waste_.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 8 horz N _row_. Build up in rank alone. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Numerica_RowStack (max_accept=1)
    :cvar Stack _reserves_: None
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=558, name='Numerica (2 decks)', game_type=GT.NUMERICA,
                    decks=2, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, **kw):
        kw.setdefault('rows', 6)
        return super(Numerica2Decks, self)._create_game(**kw)


class LadyBetty(Numerica):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 6 horz. Build any from _waste_. Play top.

    _talon_
      Pile 1 E. Deal 1 card on _waste_.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 4 horz N _row_. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: Numerica_RowStack (max_accept=1)
    :cvar Stack _reserves_: None
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=171, name='Lady Betty', game_type=GT.NUMERICA, decks=1,
                    skill_level=SL.BALANCED, version='4.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _foundation_ = stack.SS_Foundation

    def _create_game(self, **kw):
        kw.setdefault('rows', 6)
        return super(LadyBetty, self)._create_game(**kw)


class LastChance(LadyBetty):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 7 horz. Init deal 1 card. Build any 1st from _waste_ then any.
      Play top.

    _reserve_
      Pile 1 NE. Init deal 1 close card. Build none. Play top. Flip when
      _talon_ and _waste_ are gap.

    _talon_
      Pile 1 E. Deal 1 card on _waste_.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 4 horz N _row_. Build up in rank by same suit. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: LastChance_RowStack (max_accept=1)
    :cvar Stack _reserves_: LastChance_Reserve
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=589, name='Last Chance', game_type=GT.NUMERICA,
                    decks=1, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = StackWrapper(LastChance_RowStack, max_accept=1)
    _reserve_ = LastChance_Reserve

    def _create_game(self, **kw):
        kw.setdefault('rows', 7)
        return super(LastChance, self)._create_game(**kw)

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['reserves'], flip=False)
        self.s['talon'].deal_cards()


class Strategerie(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 horz. Build any from _talon_ or _reserve_. Play top.

    _reserve_
      Pile 4 vert E. Build 1 card from _talon_. Play top.

    _talon_
      Pile 1 SE. Open. Build none. Play top.

    _foundation_
      Pile 4 horz. Build up in rank alone when _talon_ is gap. Play top.
    ------
    :cvar Layout _layout_: layout.FreeCellLayout
    :cvar Stack _talon_: stack.OpenAutoTalon
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Strategerie_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: Strategerie_Reserve
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=472, name='Strategerie', game_type=GT.NUMERICA,
                    decks=1, skill_level=SL.MOSTLY_SKILL,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _layout_ = CanfieldLayout.CanfieldLayout
    _talon_ = stack.OpenAutoTalon
    _foundation_ = stack.RK_Foundation
    _row_ = StackWrapper(Strategerie_RowStack, max_accept=cs.MAX)
    _reserve_ = Strategerie_Reserve
    _hint_ = NumericaStacks.Hint

    def _create_game(self, **kw):
        kwdefault(kw, rows=4, reserves=4, texts=True)
        l = super(Strategerie, self)._create_game(**kw)
        self.sg['dropstacks'] += [s['talon']]
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].fill_stack()


class PussInTheCorner(Numerica):
    """
    Finish cards on _foundation_ within 2 turn.

    _row_
      Pile 4 NW SW NE SW _foundation_. Build any from _waste_. Play top.

    _talon_
      Pile 1 N. Open. Build none. Play top. Gap reset _row_.

    _foundation_
      Pile 4 over 2 row. Init deal base ACE. Build up in rank by same color.
      Play none.

    note::
      ⟪highlight_play⟫ disabled. ⟪autodeal⟫ waste disabled

    seealso::
      <http://en.wikipedia.org/wiki/Puss_in_the_Corner>
    ------
    :cvar Stack _talon_: OpenRedealTalon (max_rounds=2)
    :cvar Stack _waste_: None
    :cvar Stack _foundation_: stack.SC_Foundation (max_move=0)
    :cvar Stack _row_: Numerica_RowStack (max_accept=1)
    :cvar Stack _reserves_: None
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=430, name='Puss in the Corner', game_type=GT.NUMERICA,
                    decks=1, redeals=1, skill_level=SL.BALANCED,
                    version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = StackWrapper(OpenRedealTalon, max_rounds=2)
    _waste_ = None
    _foundation_ = StackWrapper(stack.SC_Foundation, max_move=0)
    _row_ = StackWrapper(Numerica_RowStack, max_accept=1)

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 5 * l.XS, l.YM + 4 * l.YS,)
        center = lambda p: (self.width - p * l.XS) / 2
        x, y = center(1), l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'se')
        l.set_rounds_text(s['talon'], 'ne')
        dx, dy = center(4), l.YS / 2
        y += dy
        for i in range(2):  # Vert
            x = dx
            for j in range(4):  # Horz
                if j in (0, 3):
                    s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
                else:
                    y += dy if j == 1 else -dy
                    s['foundations'].append(self._foundation_(x, y, game=self))
                    y -= dy if j == 1 else -dy
                x += l.XS
            y += 2 * l.YS
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by rank, suit, deck for deal _foundation_.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['foundations'])
        self.s['talon'].fill_stack()

    def _auto_deal(self, sound=True):
        return 0


class Frog(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 5 horz S _foundation_. Build any from _waste_. Play top.

    _reserve_
      Pile 1 W. Init deal 13 open card while finish ACE.

    _talon_
      Pile 1 E _reserve_. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 8 horz. Build up in rank alone. Play none.

    seealso::
      <http://en.wikipedia.org/wiki/Frog_%28game%29>
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.RK_Foundation (max_move=0)
    :cvar Stack _row_: Numerica_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: stack.OpenStack
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=355, name='Frog', game_type=GT.NUMERICA, decks=2,
                    skill_level=SL.BALANCED, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.WasteTalon
    _waste_ = StackWrapper(stack.WasteStack, max_cards=1)
    _foundation_ = StackWrapper(stack.RK_Foundation, max_move=0)
    _row_ = StackWrapper(Numerica_RowStack, max_accept=cs.MAX)
    _reserve_ = stack.OpenStack
    _hint_ = NumericaStacks.Hint

    def _create_game(self, rows=5, playcards=16, **kw):
        l, s = layout.Layout(self), self.s
        max_rows = max(l.NSUITS, 3 + rows)
        self.table_size = (l.XM + max_rows * l.XS,
                           l.YM + l.YS + l._get_stack_height(playcards, l.YS),)
        x, y, = l.XM, l.YM
        for suit in l.SUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   suit=suit))
            x += l.XS
        x = l.XM
        y += l.YS
        s['reserves'].append(self._reserve_(x, y, game=self,
                                         offset=(0, l.YOFFSET)))
        x += l.XS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        x += l.XS
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        l.defaultStackGroups()
        return l

    def _start(self, reserves=13, **kw):
        self._play_sound('deal')
        pile = self.s['reserves']
        while pile[0].cards < reserves:
            self.s['talon'].deal_row_or_drop(pile)
        self.s['talon'].deal_cards()


class Fly(Frog):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 5 horz S _foundation_. Build any from _waste_. Play top.

    _reserve_
      Pile 1 W. Init deal 13 open card.

    _talon_
      Pile 1 E _reserve_. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 8 horz. Init deal base ACE. Build up in rank alone. Play none.

    seealso::
      <http://en.wikipedia.org/wiki/Frog_%28game%29>
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Numerica_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: stack.OpenStack
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=356, name='Fly', game_type=GT.NUMERICA, decks=2,
                    skill_level=SL.BALANCED, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.RK_Foundation

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by rank, suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, reserves=13, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'])
        self._play_sound('deal')
        for i in range(reserves):
            self.s['talon'].dealRow(self.s['reserves'])
        self.s['talon'].deal_cards()


class Fanny(Frog):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 5 horz S _foundation_. Build any from _waste_. Play top.

    _reserve_
      Pile 1 W. Init deal 12 open card.

    _talon_
      Pile 1 E _reserve_. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 8 horz. Build up in rank alone. Play top.
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Numerica_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: stack.OpenStack
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=613, name='Fanny', game_type=GT.NUMERICA, decks=2,
                    skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.RK_Foundation

    def _start(self, reserves=12, flip=False, **kw):
        self._play_sound('deal')
        for i in range(reserves - 1):
            self.s['talon'].dealRow(self.s['reserves'], flip=flip)
        self.s['talon'].dealRow(self.s['reserves'])
        self.s['talon'].deal_cards()


class Gnat(Fly):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 5 horz S _foundation_. Build any from _waste_. Play top.

    _reserve_
      Pile 6 over 3 row E. Init deal 1 card.

    _talon_
      Pile 1 NW. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 8 horz N. Init deal base ACE. Build up in rank by same suit. Play
      top.

    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: Numerica_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: stack.OpenStack
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=357, name='Gnat', game_type=GT.NUMERICA, decks=1,
                    skill_level=SL.BALANCED, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.SS_Foundation

    def _create_game(self, rows=4, reserves=(2,) * 3, playcards=16, **kw):
        l, s = layout.Layout(self), self.s
        max_rows = max(2 + l.NSUITS + max(reserves), rows + max(reserves))
        self.table_size = (l.XM + max_rows * l.XS,
                           l.YM + l.YS + l._get_stack_height(playcards, l.YS))
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        x += l.XS
        for i in l.NSUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        x = l.XM + 2 * l.XS
        y += l.YS
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        dx = x
        y -= l.YS / 2
        for reserve in reserves:  # Vert
            x = dx
            for j in range(reserve):  # Horz
                s['reserves'].append(self._reserve_(x, y, game=self))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'])
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'])
        self.s['talon'].deal_cards()


class Gloaming(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 5 horz E _reserve_. Build any from _reserve_. Play top.

    _reserve_
      Pile 3 horz. Init deal [18, 17, 17] open cards. Build none. Play top.

    _foundation_
      Pile 4 horz N. Build up in rank by same suit. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: Gloaming_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: stack.OpenStack
    :cvar Hint _hint_: Gloaming_Hint
    """
    __info__ = dict(id=378, name='Gloaming', game_type=(GT.NUMERICA | GT.OPEN
                    | GT.ORIGINAL), decks=1, version='0.90fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation_ = stack.SS_Foundation
    _row_ = stack.StackWrapper(Gloaming_RowStack, max_accept=cs.MAX)
    _reserve_ = stack.OpenStack
    _hint_ = Gloaming_Hint

    def _create_game(self, reserves=3, rows=5, playcards=18, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + (reserves + rows + 1) * l.XS,
                           l.YM + l.YS + l._get_stack_height(playcards, l.YS),)
        x, y = (self.width - l.NSUITS * l.XS) / 2, l.YM
        for suit in l.SUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self,
                                                   suit=suit))
            x += l.XS
        x = l.XM
        y += l.YS
        for i in range(reserves):  # Horz
            s['reserves'].append(self._reserve_(x, y, game=self,
                                             offset=(0, l.YOFFSET)))
            x += l.XS
        x += l.XS
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        x, y = self.width - l.XS, self.height - l.YS
        s['talon'] = self._talon_(x, y, game=self)
        l.defaultAll()
        return l

    def _start(self, reserves=18, **kw):
        for i in range(reserves - 2):
            self.s['talon'].dealRow(rows=self.s['reserves'], frames=0)
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'])
        self.s['talon'].dealRowAvail(rows=reserves)


class Chamberlain(Gloaming):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 3 horz E _reserve_. Build any from _reserve_. Play top.

    _reserve_
      Pile 4 horz. Init deal 13 open cards. Build none. Play top.

    _foundation_
      Pile 4 horz N. Build up in rank alone. Play top.
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Gloaming_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: stack.OpenStack
    :cvar Hint _hint_: Gloaming_Hint
    """
    __info__ = dict(id=379, name='Chamberlain', game_type=(GT.NUMERICA |
                    GT.OPEN | GT.ORIGINAL), decks=1,
                    skill_level=SL.MOSTLY_SKILL, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.RK_Foundation

    def _create_game(self, **kw):
        kwdefault(kw, reserves=4, rows=3)
        return super(Chamberlain, self)._create_game(**kw)

    def _start(self, **kw):
        kw.setdefault('reserves', 13)
        super(Chamberlain, self)._start(**kw)


class Toad(Gloaming):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 5 horz S _foundation_. Build any from _reserve_. Play top.

    _reserve_
      Pile 13 over 5 row E. Init deal 1 open cards. Build none. Play top.

    _talon_
      Pile 1 NW. Deal 1 card on _reserve_ when all gap.

    _foundation_
      Pile 8 horz N. Build up in rank by same suit. Play top.

    note::
      Made layout like Gnat.
    ------
    :cvar Stack _talon_: Toad_Talon
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: Gloaming_RowStack (max_accept=MAX)
    :cvar Stack _reserves_: stack.OpenStack
    :cvar Hint _hint_: Gloaming_Hint
    """
    __info__ = dict(id=402, name='Toad', game_type=GT.NUMERICA, decks=2,
                    skill_level=SL.BALANCED, version='0.90fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = Toad_Talon
    _row_ = stack.StackWrapper(Gloaming_RowStack, max_accept=cs.MAX)

    def _create_game(self, rows=5, reserves=(3,) * 4 + (1,), playcards=16,
                   **kw):
        l, s = layout.Layout(self), self.s
        max_rows = max(2 + l.NSUITS + max(reserves), rows + max(reserves))
        self.table_size = (l.XM + max_rows * l.XS,
                         l.YM + max(l.YS + l._get_stack_height(playcards, l.YS),
                                     (len(reserves) + 1) * l.YS))
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 's')
        x += 2 * l.XS
        for i in l.NSUITS:  # Horz
            s['foundations'].append(self._foundation_(x, y, game=self, suit=i))
            x += l.XS
        x = l.XM + 2 * l.XS
        y += l.YS
        for i in range(rows):  # Horz
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        dx = x
        y -= l.YS / 2
        for reserve in reserves:  # Vert
            x = dx
            for j in range(reserve):  # Horz
                s['reserves'].append(self._reserve_(x, y, game=self))
                x += l.XS
            y += l.YS
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'])


class Amphibian(Chamberlain):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 5 horz S _foundation_. Build any from _reserve_. Play top.

    _reserve_
      Pile 4 horz S. Init deal 1 open cards. Build none. Play top. Gap deal 1
      card from _talon_.

    _talon_
      Pile 1 E _reserve_.

    _foundation_
      Pile 8 horz N. Build up in rank alone. Play top.
    ------
    :cvar Stack _talon_: stack.Talon
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Gloaming_RowStack (max_accept=1)
    :cvar Hint _hint_: Gloaming_Hint
    """
    __info__ = dict(id=754, name='Amphibian', game_type=(GT.NUMERICA |
                    GT.ORIGINAL), decks=2, version='1.10fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.Talon
    _row_ = stack.StackWrapper(Gloaming_RowStack, max_accept=1)

    def _create_game(self, rows=5, reserves=4, playcards=15, **kw):
        l, s = layout.Layout(self), self.s
        max_rows = max(l.NSUITS, rows, reserves + 1)
        self.table_size = (l.XM + max_rows * l.XS,
                        l.YM + 2 * l.YS + l._get_stack_height(playcards, l.YS),)
        center = lambda p: l.XM + (max_rows - p) * l.XS / 2
        x, y = center(l.NSUITS), l.YM
        for i in l.SUITS:
            s['foundations'].append(self._foundation_(x, y, game=self))
            x += l.XS
        x = center(rows)
        y += l.YS
        for i in range(rows):
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        x, y = center(reserves + 1), self.height - l.YS
        for i in range(reserves):
            s['reserves'].append(self._reserve_(x, y, game=self))
            x += l.XS
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'n')
        l.defaultStackGroups()
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow(rows=self.s['reserves'])

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _reserve_ when all gap from _talon_.

        *Parameters:
          stack : Stack
            pile just played
        """
        if all(not s['cards'] for s in self.s['reserves']):
            self.s['talon'].dealRow(rows=self.s['reserves'], sound=True)


class Shifting(Numerica):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 horz. Build any from _waste_ and down in rank alone from _row_.
      Play top. Base KING when from _row_ else any.

    _talon_
      Pile 1 E. Deal 1 card on _waste_.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 4 horz N _row_. Build up in rank alone. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack (max_cards=1)
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Shifting_RowStack (max_accept=1)
    :cvar Stack _reserves_: None
    :cvar Hint _hint_: CautiousHint
    """
    __info__ = dict(id=435, name='Shifting', game_type=GT.NUMERICA, decks=1,
                    skill_level=SL.BALANCED, version='0.91fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _row_ = StackWrapper(Shifting_RowStack, max_accept=1)
    _hint_ = NumericaStacks.CautiousHint


class Assembly(Numerica):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 4 horz. Build down in rank alone. Play top. Base from _waste_.

    _talon_
      Pile 1 E. Deal 1 card on _waste_.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top. Max 1 card.

    _foundation_
      Pile 4 horz N _row_. Build up in rank alone. Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: Assembly_RowStack (max_move=1)
    :cvar Stack _reserves_: None
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=599, name='Assembly', game_type=GT.NUMERICA, decks=1,
                    skill_level=SL.BALANCED, version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _waste_ = stack.WasteStack
    _row_ = stack.StackWrapper(Assembly_RowStack, max_move=1)
    _hint_ = CautiousDefaultHint

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class AnnoDomini(Numerica):
    """
    Finish cards on _foundation_ within 3 turn.

    _row_
      Pile 4 horz. Build down in rank by alt color wrap. Play seq.

    _talon_
      Pile 1 E. Deal 1 card on _waste_.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 4 horz N _row_. Build up in rank by same suit  wrap. Base rank of
      each suit by year (2011... TWO JACK ACE ACE). Play top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.WasteTalon (max_rounds=3)
    :cvar Stack _waste_: stack.WasteStack
    :cvar Stack _foundation_: stack.Vary_SS_Foundation
    :cvar Stack _row_: stack.AC_RowStack (mod=13)
    :cvar Stack _reserve_: None
    :cvar Hint _hint_: hint.DefaultHint
    """
    __info__ = dict(id=600, name='Anno Domini', game_type=GT.NUMERICA,
                    decks=1, redeals=2, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.StackWrapper(stack.WasteTalon, max_rounds=3)
    _waste_ = stack.WasteStack
    _foundation_ = stack.Vary_SS_Foundation
    _row_ = StackWrapper(stack.AC_RowStack, mod=13)
    _hint_ = DefaultHint

    def _create_game(self, **kw):
        import time
        l = super(AnnoDomini, self)._create_game(**kw)
        year = str(time.localtime()[0])
        for s in self.s['foundations']:
            d = int(year[s.id]) - 1
            self._save_stack_caps(s, suit=s.id, base_suit=s.id,
                                  base_rank=cs.JACK if d == -1 else d)
        l.set_rounds_text(self.s['talon'], 'nn')
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].deal_cards()


class CircleNine(game.Game):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 9 oval. Init deal 1 card. Build any from _waste_. Play top. Base
      none.

    _talon_
      Pile 1 NW. Build none. Play top.

    _foundation_
      Pile 1 center. Init deal base card. Build up in rank alone from base
      wrap. Play top.
    ------
    :cvar Stack _talon_: stack.OpenAutoTalon
    :cvar Stack _foundation_: stack.RK_Foundation (max_cards=52,
      max_move=0, mod=13, base_rank=ANY)
    :cvar Stack _row_: CircleNine_RowStack (max_accept=1, base_rank=GAP)
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=641, name='Circle Nine', game_type=GT.NUMERICA,
                    decks=1, skill_level=SL.BALANCED,
                    version='0.93fc')
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.OpenAutoTalon
    _foundation_ = stack.StackWrapper(stack.RK_Foundation, max_cards=52,
                                      max_move=0, mod=13, base_rank=cs.ANY)
    _row_ = stack.StackWrapper(CircleNine_RowStack, max_accept=1,
                               base_rank=cs.GAP)
    _hint_ = NumericaStacks.Hint

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 7 * l.XS, l.YM + 3 * l.YS,)
        ddx = l.XM + l.XS
        for dx, dy in ((2, 0), (4, 0), (6, 0), (8, 0), (10, 1), (7, 2),
                     (5, 2), (3, 2), (0, 1), ):
            x, y = l.delta_coords(x=ddx, dx=dx, dy=dy, fx=2)
            s['rows'].append(self._row_(x, y, game=self, offset=(0, 0)))
        x, y = (self.width - l.XS) / 2, l.YM + l.YS
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], 'ne')
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        l.defaultStackGroups()
        self.sg['dropstacks'] += [s['talon']]
        return l

    def _start(self, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'])
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].fill_stack()

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _talon_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromTalon(self, stack, self.s['rows'])


class Measure(CircleNine):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 horz S _foundation_. Init deal 1 card. Build any from _waste_.
      Play top. Base none.

    _talon_
      Pile 1 NW. Build none. Play top.

    _foundation_
      Pile 1 E _talon_. Build up in rank alone from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.OpenAutoTalon
    :cvar Stack _foundation_: stack.RK_Foundation (max_cards=52,
      max_move=0, mod=13, base_rank=ANY)
    :cvar Stack _row_: CircleNine_RowStack (max_accept=1, base_rank=GAP)
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=643, name='Measure', game_type=(GT.NUMERICA |
                    GT.ORIGINAL), decks=1, version='0.93fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    def _create_game(self, rows=8, playcards=10, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + rows * l.XS,
                           l.YM + l.YS + l._get_stack_height(playcards, l.YS),)
        x, y = l.XM, l.YM
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], 'ne')
        x = self.width - l.XS
        s['foundations'].append(self._foundation_(x, y, game=self))
        l.set_ncards_text(s['foundations'][0], 'nw')
        x = l.XM
        y += l.YS
        for i in range(rows):
            s['rows'].append(self._row_(x, y, game=self))
            x += l.XS
        l.defaultStackGroups()
        self.sg['dropstacks'] += [s['talon']]
        return l

    def _start(self, **kw):
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].fill_stack()


class DoubleMeasure(Measure):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 10 horz S _foundation_. Init deal 1 card. Build any from _waste_.
      Play top. Base none.

    _talon_
      Pile 1 NW. Build none. Play top.

    _foundation_
      Pile 1 E _talon_. Build up in rank alone from base wrap. Play top.
    ------
    :cvar Stack _talon_: stack.OpenAutoTalon
    :cvar Stack _foundation_: stack.RK_Foundation (max_cards=104,
      max_move=0, mod=13, base_rank=ANY)
    :cvar Stack _row_: CircleNine_RowStack (max_accept=1, base_rank=GAP)
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=644, name='Double Measure', game_type=(GT.NUMERICA |
                    GT.ORIGINAL), decks=2, version='0.93fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _foundation_ = stack.StackWrapper(stack.RK_Foundation, max_cards=104,
                                      max_move=0, mod=13, base_rank=cs.ANY)

    def _create_game(self, **kw):
        kw.setdefault('rows', 10)
        return super(DoubleMeasure, self)._create_game(**kw)


class Aglet(Numerica):
    """
    Finish cards on _foundation_ within a turn.

    _row_
      Pile 8 horz. Init deal 6 open card. Build none. Play top.

    _reserve_
      Pile 1 NW. Build any. Play top.

    _foundation_
      Pile 4 horz N _row_. Init deal base ACE. Build up in rank alone. Play
      top.

    note::
      ⟪highlight_play⟫ disabled
    ------
    :cvar Stack _talon_: stack.InitialDealTalon
    :cvar Stack _waste_: None
    :cvar Stack _foundation_: stack.RK_Foundation
    :cvar Stack _row_: stack.BasicRowStack
    :cvar Stack _reserves_: stack.ReserveStack (max_cards=MAX)
    :cvar Hint _hint_: Hint
    """
    __info__ = dict(id=760, name='Aglet', game_type=(GT.ONE_DECK |
                    GT.ORIGINAL), decks=1, version='2.00fc',
                    skill_level=SL.MOSTLY_SKILL)
    __author__ = 'Skomoroh'
    __version__ = 7.70

    _talon_ = stack.InitialDealTalon
    _foundation = stack.RK_Foundation
    _row_ = stack.BasicRowStack
    _reserve_ = stack.StackWrapper(stack.ReserveStack, max_cards=cs.MAX)

    def _create_game(self, rows=8, playcards=20, **kw):
        kw.setdefault('rows', 8)
        return super(Aglet, self)._create_game(**kw)

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return ACE on top by rank, suit, deck for _foundation_ deal.
        """
        return place.MoveToTop(cards, place.AllAce)

    def _start(self, rows=6, **kw):
        self.s['talon'].dealRow(rows=self.s['foundations'], frames=0)
        for i in range(rows - 2):
            self.s['talon'].dealRow(frames=0)
        self._play_sound('deal')
        for i in range(2):
            self.s['talon'].dealRowAvail()


class Doublets(game.Game):
    """
    Finish cards on _foundation_ with KING on _row_ within 3 turn.

    _row_
      Pile 7 E W N _foundation. Init deal 1 card. Build any 1 card. Play top.

    _talon_
      Pile 1 W. Deal 1 card on _waste_.

    _waste_
      Pile 1 E _talon_. Init deal 1 card. Build none. Play top.

    _foundation_
      Pile 1 S. Init deal base card. Build up in rank by same suit with double
      rank wrap. (A, 2, 4, 8, 3, 6, Q, J, 9, 5, 10, 7, A). Play none.
    ------
    :cvar Stack _talon_: stack.DealRowTalon
    :cvar Stack _waste_: None
    :cvar Stack _foundation_: stack.SS_Foundation
    :cvar Stack _row_: stack.AC_RowStack
    :cvar Hint _hint_: hint.CautiousDefaultHint
    """
    __info__ = dict(id=111, name="Doublets", game_type=(GT.ONE_DECK |
                    GT.GNOME), decks=1, redeals=2, skill_level=SL.MOSTLY_LUCK,
                    altnames=('Double or Quits',), version='3.10')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = StackWrapper(stack.WasteTalon, max_rounds=3)
    _waste_ = stack.WasteStack
    _foundation_ = StackWrapper(Doublets_Foundation, dir=1, max_move=0,
                                max_cards=48)
    _row_ = stack.ReserveStack
    _hint_ = CautiousDefaultHint

    def _create_game(self, **kw):
        l, s = layout.Layout(self), self.s
        self.table_size = (l.XM + 11 * l.HXS, l.YM + 4 * l.YS)
        ddx, ddy = l.XM + 5 * l.HXS, (self.height - l.YS) / 2
        for dy, dx in ((0, -1), (1, -1), (2, -1), (0, 0), (2, 0), (0, 1),
                       (2, 1)):
            x, y = l.delta_coords(x=ddx, y=ddy, dx=dx, dy=dy)
            s['rows'].append(self._row_(x, y, game=self))
        ddx += l.XS
        s['foundations'].append(self._foundation_(ddx, y, game=self))
        l.set_ncards_text(s['foundations'][-1], "s")
        x, y = l.XM, ddy
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards(s['talon'], "s")
        l.set_rounds(s['talon'], 'nn')
        x += l.XS
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards(s['wastes'][-1], "s")
        l.defaultStackGroups()

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return KING in top 8 on bottom for not base of _row_.
        """
        return place.fromTop(cards, ncards=8, rank=cs.KING)

    def _start(self):
        self._play_sound('deal')
        self.s['talon'].dealRow()
        self.s['talon'].dealRow(rows=self.s['foundations'])
        self.s['talon'].deal_cards()

    @property
    def is_complete(self):
        return (not any(p.cards for p in (s['talon'], s['wastes'][-1],)) and
                self.s['foundations'][0].is_filled)

    @state.move_state('fill')
    def _auto_fill(self, stack):
        """
        Fill stack if _row_ when gap from _waste_.

        *Parameters*:
          stack : Stack
            pile just played
        """
        fill.fromWaste(self, stack, self.s['rows'])

    def _autopiles(self, event=None):
        """
        Return piles for auto or quick move.

        Overide to disable autoflip, autodrop.

        .. \_autopiles(self, event=None) -> seq(seq(Pile ...), seq(Pile ...),
             seq(Pile ...))
        """
        return autostacks.quickplay(super(Doublets, self)._autopiles(event))


PROVIDES = [Numerica, LadyBetty, Frog, Fly, Gnat, Gloaming, Chamberlain, Toad,
            PussInTheCorner, Shifting, Strategerie, Numerica2Decks,
            LastChance, Assembly, AnnoDomini, Fanny, CircleNine, Measure,
            DoubleMeasure, Amphibian, Aglet, Doublets]